#pragma once

#include <iostream>
#include <vector>
#include <queue>
#include <memory>
#include "Thread.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Log.hpp"

// 懒汉模式线程池
namespace ThreadPoolModule
{
    using namespace ThreadModule;
    using namespace LogModule;
    using namespace CondModule;
    using namespace LogModule;

    using thread_t = std::shared_ptr<Thread>;
    const static int threadnum = 5;

    template <class T>
    class ThreadPool
    {
    private:
        bool IsEmpty()
        {
            return _taskq.empty();
        }
        // 处理任务
        void HandlerTask(std::string name)
        {
            LOG(LogLevel::DEBUG) << "线程: " << name << ", 进入HandlerTask...";
            while (true)
            {
                T t;
                {
                    LockGuard lockguard(_lock);     // 在任务队列中拿任务需要加锁保护
                    while (IsEmpty() && _isrunning) // 只有任务队列为空 && 线程池在运行，线程才需要等待休眠
                    {
                        _wait_num++;
                        _cond.Wait(_lock);
                        _wait_num--;
                    }
                    if (IsEmpty() && !_isrunning) // 只有任务队列为空 && 线程池退出，所有的线程才不需要等待
                        break;
                    t = _taskq.front();
                    _taskq.pop();
                }
                t(); // 线程有独立栈，处理任务不需要被保护
            }
            LOG(LogLevel::INFO) << "线程：" << name << "，退出...";
        }

        ThreadPool(int num = threadnum)
            : _num(num), _wait_num(0), _isrunning(false)
        {
            for (int i = 0; i < _num; i++)
            {
                // 非静态成员函数取函数指针需要加&
                _threads.push_back(std::make_shared<Thread>(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1)));
                LOG(LogLevel::INFO) << "创建线程：" << _threads.back()->Name();
            }
        }

    public:

		// 不支持拷贝、赋值
        ThreadPool(const ThreadPool<T>&) = delete;
        const ThreadPool<T>& operator=(const ThreadPool<T>&) = delete;

        // 1.将构造函数私有化，让其不能在类外实例化对象，只能在类里面创建一个对象
        // 2.创建单例对象的函数设为静态函数，在类外通过指定类域的方式访问这个静态成员函数，获取单例对象
        // 3.因为构造函数被私有，类外不能创建对象，非静态成员函数只能通过对象访问，不能通过指定类域的方式访问
        static ThreadPool<T> *GetInstance()
        {
            if (_instance == nullptr)
            {
                LockGuard lockguard(_mtx);
                if (_instance == nullptr)
                {
                    LOG(LogLevel::INFO) << "单例首次被执行，需要加载对象...";
                    _instance = new ThreadPool<T>();
                    _instance->Start();
                }
            }
            return _instance;
        }

        // void Equeue(const T& in)
        // {
        //     LockGuard lockguard(_lock);
        //     if (_isrunning)
        //     {
        //         _taskq.push(in);
        //         if (_wait_num > 0)
        //         {
        //             _cond.Notify();
        //         }
        //     }
        // }

        void Equeue(const T&& in)
        {
            LockGuard lockguard(_lock);
            if (_isrunning)
            {
                _taskq.push(in);
                if (_wait_num > 0)
                {
                    _cond.Notify();
                }
            }
        }

        void Start()
        {
            LockGuard lockguard(_lock);
            if (_isrunning)
                return;
            _isrunning = true;
            for (auto &threadptr : _threads)
            {
                threadptr->Start();
                LOG(LogLevel::INFO) << "启动线程：" << threadptr->Name();
            }
        }

        void Wait()
        {
            for (auto &threadptr : _threads)
            {
                threadptr->Join();
                LOG(LogLevel::INFO) << "回收线程：" << threadptr->Name();
            }
        }

        void Stop()
        {
            LockGuard lockguard(_lock);
            if (_isrunning)
            {
                // 退出线程池必须保证
                // 1.不能再进任务
                _isrunning = false;
                // 2.线程池内的任务必须全部处理完
                // 3.让线程自己退出（被唤醒）
                if (_wait_num > 0)
                {
                    _cond.NotifyAll(); // 将等待中的线程全部唤醒，如果有任务处理完剩余任务，线程正常回收
                }
            }
        }

        ~ThreadPool()
        {}

    private:
        std::vector<thread_t> _threads; // 管理线程
        std::queue<T> _taskq;           // 管理任务
        int _num;                       // 线程个数
        int _wait_num;                  // 正在等待任务的线程个数
        Mutex _lock;
        Cond _cond;      // 等待任务
        bool _isrunning; // 线程池状态

        static ThreadPool<T>* _instance; // 单例对象
        static Mutex _mtx;
    };

    template<class T>
    ThreadPool<T>* ThreadPool<T>::_instance = nullptr;

    template<class T>
    Mutex ThreadPool<T>::_mtx;
}
